package org.example;

import org.example.util.StringToByteArray;

import java.io.IOException;
import java.net.*;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.*;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class UdpClient implements Runnable {

    private SocketChannel channel;

    private AtomicBoolean state = new AtomicBoolean(false);

    private LinkedBlockingQueue<Integer> channelQueue = new LinkedBlockingQueue<>();

    InetSocketAddress broadcast = InetSocketAddress.createUnresolved("255.255.255.255", Config.instance.getWar3Port());

    private ReadChannel readChannel;
    private ReadWarChannel readWarChannel;

    @Override
    public void run() {

        readChannel = new ReadChannel();
        Thread thread = new Thread(readChannel);
        thread.setName("readChannel");
        thread.start();

        readWarChannel = new ReadWarChannel();
        Thread readWarChannelThread = new Thread(readWarChannel);
        readWarChannelThread.setName("readWarChannelThread");
        readWarChannelThread.start();


        try {
            //接收魔兽争霸3发送广播(这个广播是在创建房间是发送的)
            InetAddress inetAddress = InetAddress.getLocalHost();
            System.out.println("本地计算机的IP地址是: " + inetAddress);

            SocketAddress socketAddress = new InetSocketAddress(inetAddress, Config.instance.getWar3Port());
            DatagramSocket socket = new DatagramSocket(socketAddress);
            byte[] receiveData = new byte[65507];

            while (true) {

                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                // 通过 receive() 方法阻塞等待接收数据
                socket.receive(receivePacket);

                //连接服务器
                connectService();
                //将广播发到服务器上
                if (channel != null && channel.isConnected()) {
                    ByteBuffer buffer = buildBuffer(broadcast.toString(), receivePacket.getData(), receivePacket.getLength());
                    try {
                        channel.write(buffer);
                    }catch (Exception e){

                    }

                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void connectService() throws IOException {
        if ((channel == null || !channel.isConnected()) && readChannel.selector != null) {
            //防止多次创建
            if (!state.getAndSet(true)) {
                if (channel == null || !channel.isConnected()) {

                    channel = SocketChannel.open();
                    channel.configureBlocking(false);
                    channel.connect(new InetSocketAddress(Config.instance.getServerIp(), Config.instance.getServerUdpPort()));
                    readChannel.registerConnect(channel);

                }
            }

        }
    }

    class ReadChannel implements Runnable {
        ConcurrentLinkedQueue<AbstractSelectableChannel> registerQueue = new ConcurrentLinkedQueue<>();

        private DatagramChannel localChannel;

        private String dstAddress;

        private AtomicBoolean localState = new AtomicBoolean(false);

        private Selector selector;

        @Override
        public void run() {
            try {

                selector = Selector.open();

                //尝试连接服务端
                connectService();

                while (!Thread.interrupted()) {
                    // 选择就绪的通道
                    selector.select();

                    // 获取选择键集合
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                    while (keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();

                        if (key.isConnectable()) {
                            SocketChannel socketChannel = (SocketChannel) key.channel();
                            try {
                                // 完成连接[这里会出现超时异常]
                                socketChannel.finishConnect();

                                //切换事件
                                ByteBuffer buffer = ByteBuffer.allocate(4);
                                socketChannel.register(selector, SelectionKey.OP_READ, buffer);

                                //写入该应用端口
                                ByteBuffer allocate = ByteBuffer.allocate(4 + 4 + 1);
                                allocate.putInt(4 + 1);
                                allocate.putInt(Config.instance.getAppPort());
                                allocate.put(new byte[]{0});
                                allocate.flip();
                                socketChannel.write(allocate);


                            } catch (Exception ex) {
                                ex.printStackTrace();
                                socketChannel.close();
                            }
                            state.set(false);


                        } else if (key.isReadable()) {

                            SelectableChannel selectableChannel = key.channel();
                            if (selectableChannel instanceof DatagramChannel) {
                                // 读取数据
                                SocketChannel socketChannel = (SocketChannel) key.attachment();
                                ByteBuffer buffer = ByteBuffer.allocate(65507);
                                DatagramChannel dc = (DatagramChannel) key.channel();
                                int read = -1;
                                try{
                                    read = dc.read(buffer);
                                }catch (Exception e){

                                }
                                if(read == -1){
                                    dc.close();
                                }else{
                                    buffer.flip();
                                    byte[] data = new byte[read];
                                    buffer.get(data);


                                    if (data.length > 100) {


                                        ByteBuffer allocate = ByteBuffer.allocate(2);
                                        allocate.order(ByteOrder.LITTLE_ENDIAN);
                                        allocate.putShort(Config.instance.getAppPort().shortValue());
                                        byte[] portArray = allocate.array();

                                        //修改端口
                                        data[data.length - 2] = portArray[0];
                                        data[data.length - 1] = portArray[1];
                                    }



                                    ByteBuffer bf = buildBuffer(dstAddress, data, data.length);
                                    try{
                                        socketChannel.write(bf);
                                    }catch (Exception e){
                                        e.printStackTrace();
                                    }


                                    String s = StringToByteArray.byteArrayToHexString(data, data.length);
                                    System.out.println("Local from : " + s);
                                }


                            } else {
                                ByteBuffer buffer = (ByteBuffer) key.attachment();
                                SocketChannel clientChannel = (SocketChannel) key.channel();

                                int len = -1;

                                try{
                                     len = clientChannel.read(buffer);
                                }catch (Exception e){
                                    System.out.println("len:"+len);
                                    e.printStackTrace();
                                }

                                if (len == -1) {
                                    clientChannel.close();

                                } else if (len > 0) {

                                    if (buffer.limit() == buffer.position()) {
                                        buffer.flip();

                                        if (buffer.limit() == 4) {
                                            int dataLen = buffer.getInt();
                                            System.out.println("dataLen:" + dataLen + ",hash:" + clientChannel.hashCode() + ",len:" + len);
                                            ByteBuffer newBuffer = ByteBuffer.allocate(dataLen);
                                            key.attach(newBuffer);

                                        }  else {

                                            int anInt = buffer.getInt();
                                            byte[] addressByte = new byte[anInt];
                                            buffer.get(addressByte);
                                            dstAddress = new String(addressByte);

                                            byte[] bt = new byte[buffer.remaining()];
                                            buffer.get(bt);

                                            if(bt.length == 5){
                                                ByteBuffer wrap = ByteBuffer.wrap(bt);
                                                byte b = wrap.get();
                                                int port = wrap.getInt();

                                                if(b == 1){

                                                    System.out.println("连接本地客户端获取连接");

                                                    //先尝试本地连接魔兽争霸3
                                                    SocketChannel warChanel = SocketChannel.open();
                                                    warChanel.configureBlocking(false);
                                                    warChanel.connect(new InetSocketAddress("127.0.0.1", Config.instance.getWar3Port()));
                                                    Object[] objects = new Object[]{warChanel,clientChannel,dstAddress,port};

                                                    readWarChannel.registerConnect(objects);

                                                }else{
                                                    System.out.println("收到连接");
                                                    channelQueue.offer(1);
                                                }


                                                ByteBuffer newBuffer = ByteBuffer.allocate(4);
                                                key.attach(newBuffer);

                                            }else{
                                                if(bt.length>100){
                                                    ByteBuffer allocate = ByteBuffer.allocate(2);
                                                    allocate.order(ByteOrder.LITTLE_ENDIAN);
                                                    allocate.put(bt,bt.length-2,2);
                                                    allocate.flip();
                                                    Short aShort = allocate.getShort();
                                                    int p = aShort.intValue();


                                                    TcpClient tcpClient = TcpClient.tcpClientMap.get(p);
                                                    if(tcpClient == null){
                                                        TcpClient t = new TcpClient(p);
                                                        TcpClient.tcpClientMap.putIfAbsent(p,t);
                                                    }
                                                    TcpClient tcpClient1 = TcpClient.tcpClientMap.get(p);
                                                    tcpClient1.setDstAddress(dstAddress);
                                                    tcpClient1.setDstChannel(clientChannel);
                                                    tcpClient1.setChannelQueue(channelQueue);

                                                }

                                                String s = StringToByteArray.byteArrayToHexString(bt, bt.length);
                                                System.out.println("Server message from  :" + s);


                                                //尝试连接本地服务
                                                if (localChannel == null || !localChannel.isConnected()) {
                                                    //防止多次创建
                                                    if (!localState.getAndSet(true)) {
                                                        if (localChannel == null || !localChannel.isConnected()) {
                                                            localChannel = DatagramChannel.open();
                                                            localChannel.configureBlocking(false);
                                                            localChannel.connect(new InetSocketAddress("127.0.0.1", Config.instance.getWar3Port()));
                                                            selector.wakeup();
                                                            localChannel.register(selector, SelectionKey.OP_READ, clientChannel);
                                                            localState.set(false);

                                                        }
                                                    }
                                                }
                                                if (localChannel != null && localChannel.isConnected() && localChannel.isOpen()) {
                                                    ByteBuffer wrap = ByteBuffer.wrap(bt);
                                                    localChannel.write(wrap);

                                                }

                                                ByteBuffer newBuffer = ByteBuffer.allocate(4);
                                                key.attach(newBuffer);
                                            }
                                        }
                                    }
                                }

                            }

                        }

                        // 移除选择键
                        keyIterator.remove();
                    }

                    AbstractSelectableChannel channel = null;
                    while ((channel = registerQueue.poll()) != null) {
                        channel.register(selector, SelectionKey.OP_CONNECT);
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void registerConnect(SocketChannel socketChannel) {
            registerQueue.offer(socketChannel);
            selector.wakeup();
        }
    }

    class ReadWarChannel implements Runnable {

        ConcurrentLinkedQueue<Object[]> registerQueue = new ConcurrentLinkedQueue<>();

        private Selector selector;


        @Override
        public void run() {
            try {

                selector = Selector.open();


                while (!Thread.interrupted()) {
                    // 选择就绪的通道
                    selector.select();

                    // 获取选择键集合
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                    while (keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();

                        if (key.isConnectable()) {
                            SocketChannel socketChannel = (SocketChannel) key.channel();
                            Object[] attachment = (Object[]) key.attachment();
                            try {
                                // 完成连接[这里会出现超时异常]
                                socketChannel.finishConnect();

                                if (attachment.length == 4) {

                                    //连接服务端
                                    SocketChannel cloudChannel = SocketChannel.open();
                                    cloudChannel.configureBlocking(false);
                                    cloudChannel.connect(new InetSocketAddress(Config.instance.getServerIp(), Config.instance.getServerTcpPort()));

                                    Object[] objects = new Object[5];
                                    System.arraycopy(attachment, 0, objects, 0, attachment.length);
                                    objects[4] = socketChannel;

                                    cloudChannel.register(selector, SelectionKey.OP_CONNECT, objects);


                                } else {

                                    //告诉服务端
                                    int port = (int)attachment[3];
                                    ByteBuffer allocate = ByteBuffer.allocate(1 + 4 +4);
                                    allocate.put(new byte[]{1});
                                    allocate.putInt(Config.instance.getAppPort());
                                    allocate.putInt(port);
                                    allocate.flip();
                                    socketChannel.write(allocate);


                                    SocketChannel war3Channel = (SocketChannel)attachment[4];
                                    //交换数据
                                    war3Channel.register(selector, SelectionKey.OP_READ, socketChannel);
                                    socketChannel.register(selector, SelectionKey.OP_READ, war3Channel);


                                }


                            } catch (Exception ex) {
                                ex.printStackTrace();
                                socketChannel.close();

                                //告诉客户端连接失败
                                String address = (String)attachment[2];
                                SocketChannel sc = (SocketChannel)attachment[1];
                                byte[] bt = new byte[]{3};
                                ByteBuffer byteBuffer = buildBuffer(address, bt, bt.length);
                                sc.write(byteBuffer);

                            }


                        } else if (key.isReadable()) {

                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                            SocketChannel srcChannel = (SocketChannel) key.channel();
                            SocketChannel dstChannel = (SocketChannel) key.attachment();

                            int len = srcChannel.read(byteBuffer);
                            if (len == -1) {
                                srcChannel.close();
                            } else if (len > 0) {
                                byteBuffer.flip();
                                dstChannel.write(byteBuffer);
                            }
                        }
                        // 移除选择键
                        keyIterator.remove();
                    }

                    Object[] objects = null;
                    while ((objects = registerQueue.poll()) != null) {
                        SocketChannel sc = (SocketChannel)objects[0];
                        sc.register(selector, SelectionKey.OP_CONNECT, objects);
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        public void registerConnect(Object[] objects) {
            registerQueue.offer(objects);
            selector.wakeup();
        }

    }

    private ByteBuffer buildBuffer(String inetSocketAddress, byte[] bt, int len) {
        byte[] bByte = inetSocketAddress.getBytes();
        ByteBuffer allocate = ByteBuffer.allocate(4 + 4 + bByte.length + len);
        allocate.putInt(4 + bByte.length + len);
        allocate.putInt(bByte.length);
        allocate.put(bByte);
        allocate.put(bt, 0, len);
        allocate.flip();
        return allocate;
    }


}
